/*
 * Copyright (C) 2012-2016 TeraCN
 * 
 * This file is part of TeraCN.
 * 
 * TeraCN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * TeraCN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package tera.gameserver.network;

import java.util.HashSet;

import tera.gameserver.network.clientpackets.*;

import rlib.logging.Logger;
import rlib.logging.Loggers;
import rlib.util.pools.FoldablePool;
import rlib.util.pools.Pools;

/**
 * @author Ronn Mod Evestu
 */
public enum ClientPacketType
{
	C_GUARD_PK_POLICY(0x6312, new C_Guard_Pk_Policy()),
	C_SIMPLE_TIP_REPEAT_CHECK(0xD482, new C_Simple_tip_Repeat_Check()),
	C_TRADE_BROKER_HIGHEST_ITEM_LEVEL(0xCC38, new C_Trade_Broker_highest_Item_Level()),
	C_SET_VISIBLE_RANGE(0x8A20, new C_Set_Visible_Range()),
	C_UNION_SUMMARY(0xC25D, new C_Union_Summary()),
	C_UPDATE_CONTENTS_PLAYTIME(0xB703, new C_Update_Contents_Playtime()),
	C_PLAYER_LOCATION(0xC916, new PlayerMove()),
	C_BROADCAST_CLIMBING(0xA8A5, new PlayerClimb()),
	C_START_CLIMBING(0x55BD, new RequestStartClimb()),
	C_END_CLIMBING(0x8BBF, new PlayerClimbEnd()),
	C_NOTIFY_LOCATION_IN_ACTION(0xBB0D, new PlayerMoveOnSkill()),
	C_CHAT(0x70F8, new PlayerSay()),
	PLAYER_PRIVATE_SAY(0xCADA, new PrivateSay()),
	UPDATE_TITLE(0x9CD4, new UpdateTitle()),
	REQUEST_WORLD_ZONE(0x5E43, new RequestWorldZone()),
	REQUEST_STATE(0xAB5E, new RequestState()),
	C_SHOW_ITEM_TOOLTIP_EX(0x7E27, new RequestInventoryInfo()),
	REQUEST_SHOW_INVEN(0x9941, new RequestInventoryInfo()),
	REQUEST_DRESSING_ITEM(0xC7B5, new RequestDressingItem()),
	REQUEST_TAKING_ITEM(0xB983, new RequestDressingItem()),
	REQUEST_INVENTORY_MOVING_ITEM(0x9BDA, new RequestInventoryMovingItem()),
	REQUEST_BANK_MOVING_ITEM(0xEFC9, new RequestBankMovingItem()),
	REQUEST_BANK_CHANGE_TAB(0xDBB0, new RequestBankChangeTab()),
	REQUEST_SORT_INVENTORY(0xA92D, new RequestSortInventory()),
	REQUEST_DELETE_ITEM(0xD9EC, new RequestDeleteItem()),
	REQUEST_INVENTORY_ITEM_INFO(0xBC87, new RequestInventoryInfoItem()),
	C_TRY_LOOT_DROPITEM(0xC6A2, new RequestPickUpItem()),
	REQUEST_USE_ITEM(0xE307, new RequestUseItem()),
	REQUEST_USE_SCROLL(0x8386, new RequestUseScroll()),
	REQUEST_ITEM_TEMPLATE_INFO(0xA9DA, new RequestItemTemplateInfo()),
	REQUEST_START_EMOTION(0xA2F1, new RequestStartEmotion()),
	REQUEST_GUILD_INFO(0x81A4, new RequestGuildInfo()),
	REQUEST_GUILD_LEAVE(0x9EBD, new RequestGuildLeave()),
	REQUEST_GUILD_EXLUDE(0x8EB7, new RequestGuildExclude()),
	REQUEST_GUILD_UPDATE_RANK(0xFF13, new RequestGuildUpdateRank()),
	REQUEST_GUILD_REMOVE_RANK(0x81BD, new RequestGuildRemoveRank()),
	REQUEST_GUILD_CHANGE_RANK(0xD3D0, new RequestGuildChangeRank()),
	REQUEST_GUILD_CREATE_RANK(0x7A56, new RequestGuildCreateRank()),
	REQUEST_GUIL_LOAD_ICON(0xF883, new RequestGuildLoadIcon()),
	REQUEST_GUILD_ICON_INFO(0xAC93, new RequestGuildIcon()),
	REQUEST_GUILD_MAKE_LEADER(0xAD5E, new RequestGuildMakeLeader()),
	REQUEST_GUILD_UPDATE_TITLE(0xBACE, new RequestGuildUpdateTitle()),
	REQUEST_GUILD_UPDATE_MESSAGE(0xFDC7, new RequestGuildUpdateMessage()),
	REQUEST_GUILD_UPDATE_NOTE(0x4FE9, new RequestGuildUpdateNote()),
	C_START_SKILL(0x9344, new RequestUseShortSkill()),
	REQUEST_USE_QUEUE_SKILL(0xCED8, new RequestUseQueueSkill()),
	REQUEST_USE_RANGE_SKILL(0x7017, new RequestUseRangeSkill()),
	REQUEST_USE_RUSH_SKILL(0xBF14, new RequestUseRushSkill()),
	REQUEST_USE_DEFENSE_SKILL(0x57E6, new RequestUseDefenseSkill()),
	REQUEST_LOCK_ON_TARGET(0xD1F2, new RequestLockOnTarget()),
	REQUEST_SKILL_ACTION(0xA6B8, new RequestSkillAction()),
	C_SAVE_CLIENT_USER_SETTING(0X69CD, new C_Save_Client_User_Setting()),
	UPDATE_CLIENT_SETTING(0xCEF8, new UpdateClientSetting()),
	REQUEST_RESSURECT(0x50D1, new RequestRessurect()),
	C_REIGN_INFO(0xF275, new RequestConfirmServer()),
	C_VISIT_NEW_SECTION(0x7CBC, new RequestServerCheck()),
	REQUEST_RESTART(0xF8B1, new RequestRestart()),
	C_EXIT(0xB2D1, new RequestClientClose()),
	REQUEST_LOCAL_TELEPORT(0xCADC, new RequestLocalTeleport()),
	REQUEST_DUEL_CANCEL(0xF0AE, new RequestDuelCancel()),
	C_NPC_CONTACT(0xB405, new RequestNpcInteraction()),
	C_DIALOG(0x5D41, new RequestNpcLink()),
	REQUEST_NPC_ADD_BUY_SHOP(0xF406, new RequestNpcAddBuyShop()),
	REQUEST_NPC_SUB_BUY_SHOP(0xA087, new RequestNpcSubBuyShop()),
	REQUEST_NPC_ADD_SELL_SHOP(0x8EC1, new RequestNpcAddSellShop()),
	REQUEST_NPC_SUB_SELL_SHOP(0x51D3, new RequestNpcSubSellShop()),
	REQUEST_NPC_CONFIRM_SHOP(0x8082, new RequestNpcConfirmShop()),
	REQUEST_NPC_CONFIRM_SKILL_SHOP(0xC456, new RequestNpcConfirmSkillShop()),
	REQUEST_NPC_BANK_ADD(0xF25D, new RequestBankAdd()),
	REQUEST_NPC_BANK_SUB(0xEF1D, new RequestBankSub()),
	C_TOGGLE_TASK_INFO_WINDOW(0x6278, new RequestUpdateQuestPanel()),
	C_CANCEL_QUEST(0x83B5, new RequestCancelQuest()),
	CLIENT_SELECT_SKILL_LEARN(0xE7E0, new SelectSkillLearn()),
	REQUEST_NPC_START_PEGAS_FLY(0xC762, new RequestNpcStartPegasFly()),
	REQUEST_DIALOG_CANCEL(0xBFB9, new RequestDialogCancel()),
	REQUEST_TRADE_LOCK(0x637C, new RequestTradeLock()),
	REQUEST_TRADE_ADD_ITEM(0x683A, new RequestTradeAddItem()),
	ASSENT_TRADE(0x65A6, new AssentTrade()),
	CANCEL_TRADE(0x72D6, new CancelTrade()),
	C_END_MOVIE(0xEA66, new QuestMovieEnded()),
	REQUEST_ACTION_INVITE(0xADE2, new RequestActionInvite()),
	REQUEST_ACTION_AGREE(0xA05D, new RequestActionAgree()),
	REQUEST_ACTION_CANCEL(0xE58F, new RequestActionCancel()),
	REQUEST_PARTY_INVITE(0x0111, new RequestPartyInvite()),
	REQUEST_PARTY_LEAVE(0x8002, new RequestPartyLeave()),
	REQUEST_PARTY_CHANGE(0x784F, new RequestPartyChange()),
	REQUEST_PARTY_MAKE_LEADER(0xB14B, new RequestPartyMakeLeader()),
	REQUEST_PARTY_KICK(0xFD15, new RequestPartyKick()),
	REQUEST_PARTY_DISBAND(0xC041, new RequestPartyDisband()),
	REQUEST_COLLECT_RESOURSE(0x94FD, new RequestCollectResourse()),
	REQUEST_FRIEND_LIST(0xAB9A, new RequestFriendList()),
	REQUEST_FRIEND_ADD(0xDE4A, new RequestFriendAdd()),
	REQUEST_FRIEND_REMOVE(0x5FA8, new RequestFriendRemove()),
	
	REQUEST_DELETE_PLAYER(0xD5F7, new RequestDeletePlayer()),
	C_GET_USER_LIST(0xB7BD, new RequestPlayerList()),
	
	C_CHECK_USERNAME(0xA825, new NameChange()),
	CLIENT_KEY(0xFFFF, new ClientKey()),
	REQUEST_ADD_ENCHANT_ITEM(0x5E4E, new RequestAddEnchantItem()),
	ENCHANT_FINISH(0xDDB4, new EnchantFinish()),
	
	C_CAN_CREATE_USER(0xF14C, new RequestNewCreatePlayer()),
	
	/////////////// 国服///////////////////////////////
	C_LOGIN_ARBITER(0xD12C, new RequestAuthLogin()), // 登陆请求
	C_ENTER_SELECT_PROFESSION(0xF206, new C_Enter_Select_Profession()), // 进入选择职业界面
	C_STR_EVALUATE_LIST(0xE77B, new CanBeUsedName()), // 检查用户名是否可用
	C_CREATE_USER(0xAEBD, new RequestCreatePlayer()), // 创建角色
	C_SELECT_USER(0x5B19, new SelectedPlayer()), // 选择角色 开始游戏
	C_SELECT_USER_CONFIRM(0xE02E, new C_Select_User_Confirm()),
	
	C_LOAD_TOPO_FIN(0x7CFA, new EnteredWorld()),;
	
	private static final Logger log = Loggers.getLogger(ClientPacketType.class);
	private static ClientPacket[] packets;
	
	/**
	 * Method createPacket.
	 * @param opcode int
	 * @return ClientPacket
	 */
	public static ClientPacket createPacket(int opcode)
	{
		final ClientPacket packet = packets[opcode];
		return packet == null ? null : packet.newInstance();
	}
	
	public static void init()
	{
		final HashSet<Integer> set = new HashSet<>();
		final ClientPacketType[] elements = values();
		
		for (ClientPacketType packet : elements)
		{
			final int index = packet.getOpcode();
			
			if (set.contains(index))
			{
				log.warning("found duplicate opcode " + index + " or " + Integer.toHexString(packet.getOpcode()) + " for " + packet + "!");
			}
			
			set.add(index);
		}
		
		set.clear();
		packets = new ClientPacket[(Short.MAX_VALUE * 2) + 2];
		
		for (ClientPacketType element : elements)
		{
			packets[element.getOpcode()] = element.getPacket();
		}
		
		log.info("client packets prepared.");
	}
	
	private final FoldablePool<ClientPacket> pool;
	
	private final ClientPacket packet;
	
	private int opcode;
	
	/**
	 * Constructor for ClientPacketType.
	 * @param opcode int
	 * @param packet ClientPacket
	 */
	private ClientPacketType(int opcode, ClientPacket packet)
	{
		this.opcode = opcode;
		this.packet = packet;
		this.packet.setPacketType(this);
		pool = Pools.newConcurrentFoldablePool(ClientPacket.class);
	}
	
	/**
	 * Method getOpcode.
	 * @return int
	 */
	public final int getOpcode()
	{
		return opcode;
	}
	
	/**
	 * Method getPacket.
	 * @return ClientPacket
	 */
	public final ClientPacket getPacket()
	{
		return packet;
	}
	
	/**
	 * Method getPool.
	 * @return FoldablePool<ClientPacket>
	 */
	public final FoldablePool<ClientPacket> getPool()
	{
		return pool;
	}
	
	/**
	 * Method setOpcode.
	 * @param opcode int
	 */
	public final void setOpcode(int opcode)
	{
		this.opcode = opcode;
	}
}